package com.wwtech.epp.data.api;

import com.google.gson.JsonObject;
import com.jakewharton.retrofit2.adapter.rxjava2.HttpException;
import com.ninjia.ninjiabaselibrary.utils.VersionUtil;
import com.socks.library.KLog;
import com.wwtech.epp.application.AppConfig;
import com.wwtech.epp.contain.ContainValue;
import com.wwtech.epp.entity.AllNode;
import com.wwtech.epp.entity.Approve;
import com.wwtech.epp.entity.ApproveState;
import com.wwtech.epp.entity.BaseBack;
import com.wwtech.epp.entity.Comment;
import com.wwtech.epp.entity.CreateGroup;
import com.wwtech.epp.entity.Daily;
import com.wwtech.epp.entity.Freinds;
import com.wwtech.epp.entity.GroupInfo;
import com.wwtech.epp.entity.GroupMemberme;
import com.wwtech.epp.entity.Groupme;
import com.wwtech.epp.entity.LoadFile;
import com.wwtech.epp.entity.NodeInfo;
import com.wwtech.epp.entity.NodeMember;
import com.wwtech.epp.entity.Notice;
import com.wwtech.epp.entity.ProjectDetail;
import com.wwtech.epp.entity.ProjectEntity;
import com.wwtech.epp.entity.SignConfig;
import com.wwtech.epp.entity.TaskList;
import com.wwtech.epp.entity.TodaySign;
import com.wwtech.epp.entity.Token;
import com.wwtech.epp.entity.UserInfo;
import com.wwtech.epp.entity.UserLogin;
import com.wwtech.epp.entity.WorkCategory;
import com.wwtech.epp.utils.MD5Util;
import com.wwtech.epp.utils.RSAUtil;
import com.wwtech.epp.utils.ToastUtil;

import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.inject.Inject;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.ObservableSource;
import io.reactivex.ObservableTransformer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.http.QueryMap;

/**
 * @author hu
 * @desc 对Request实体(不执行)在执行时所调度的线程，以及得到ResponseBody后根据retCode对Result进行进一步处理
 * @date 2017/5/31 16:56
 */
public class HttpAPIWrapper {

    private HttpApi mHttpAPI;

    @Inject
    public HttpAPIWrapper(HttpApi mHttpAPI) {
        this.mHttpAPI = mHttpAPI;
    }

    //***************************************user******************************//
    public Observable<UserLogin> userLogin(Map map) {
        return wrapper(mHttpAPI.userLogin(map)).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<BaseBack> changePwd(Map map) {
        return wrapper(mHttpAPI.changePwd(map)).compose(SCHEDULERS_TRANSFORMER);
    }
    //***************************************user******************************//


    //***************************************im******************************//
    public Observable<Token> getImToken(Map map) {
        return wrapper(mHttpAPI.getImToken(addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<Freinds> getFriends(String uid, Map map) {
        return wrapper(mHttpAPI.getFriends(uid, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }

    public Observable<Groupme> getGroup(String uid, Map map) {
        return wrapper(mHttpAPI.getGroup(uid, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<GroupMemberme> getGroupMember(String groupId, Map map) {
        return wrapper(mHttpAPI.getGroupMember(groupId, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<NodeMember> getNodeMember(String groupId, Map map) {
        return wrapper(mHttpAPI.getNodeMember(groupId, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<NodeInfo> getNodeInfo(String groupId, Map map) {
        return wrapper(mHttpAPI.getNodeInfo(groupId, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<GroupInfo> getGroupInfo(String groupId, Map map) {
        return wrapper(mHttpAPI.getGroupInfo(groupId, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<Notice> getNotice(String uid, Map map) {
        return wrapper(mHttpAPI.getNotice(uid, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<BaseBack> OpFreind(Map map) {
        return wrapper(mHttpAPI.OpFreind(addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<CreateGroup> createGroup(Map map) {
        return wrapper(mHttpAPI.createGroup(map)).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<BaseBack> modifyGroup(Map map) {
        return wrapper(mHttpAPI.modifyGroup(map)).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<BaseBack> quitGroup(Map map) {
        return wrapper(mHttpAPI.quitGroup(map)).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<BaseBack> deleteGroupMember(String groupId, Map map) {
        return wrapper(mHttpAPI.deleteGroupMember(groupId, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<BaseBack> addGroupMember(String groupId, Map map) {
        return wrapper(mHttpAPI.addGroupMember(groupId, map)).compose(SCHEDULERS_TRANSFORMER);
    }
    //***************************************im******************************//


    //***************************************work******************************//
    public Observable<WorkCategory> getWorkCategory() {
        return wrapper(mHttpAPI.getWorkCategory(addParams(new HashMap<String, String>()))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<ApproveState> getApproveInfo(String requestId, Map map) {
        return wrapper(mHttpAPI.getApproveInfo(requestId, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<Approve> getApproveMyRequest(String uid, Map map) {
        return wrapper(mHttpAPI.getApproveMyRequest(uid, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }

    public Observable<Approve> getApproveRemindMeReview(String uid, Map map) {
        return wrapper(mHttpAPI.getApproveRemindMeReview(uid, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }

    public Observable<Approve> getApproveReview(String uid, Map map) {
        return wrapper(mHttpAPI.getApproveReview(uid, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<Approve> getApproveReviewHistory(String uid, Map map) {
        return wrapper(mHttpAPI.getApproveReviewHistory(uid, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<Daily> getReceiveReport(String uid, Map map) {
        return wrapper(mHttpAPI.getReceiveReport(uid, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<Daily> getMyReport(String uid, Map map) {
        return wrapper(mHttpAPI.getMyReport(uid, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<BaseBack> removeApprove(Map map) {
        return wrapper(mHttpAPI.removeApprove(addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<BaseBack> operateApprove(Map map) {
        return wrapper(mHttpAPI.operateApprove(addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<WorkCategory> getUIDesc(String dataId, Map map) {
        return wrapper(mHttpAPI.getUIDesc(dataId, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<BaseBack> upLoadAppData(Map map) {
        return wrapper(mHttpAPI.upLoadAppData(addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    //***************************************work******************************//


    //***************************************sign******************************//
    public Observable<SignConfig> getSignConfig(Map map) {
        return wrapper(mHttpAPI.getSignConfig(addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<BaseBack> updateSign(Map map) {
        return wrapper(mHttpAPI.updateSign(addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<TodaySign> getSign(String uid, Map map) {
        return wrapper(mHttpAPI.getSign(uid, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<BaseBack> getSignList(String uid, Map map) {
        return wrapper(mHttpAPI.getSignList(uid, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    //***************************************sign******************************//


    //***************************************job******************************//
    public Observable<BaseBack> jobAdd(Map map) {
        return wrapper(mHttpAPI.jobAdd( addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<TaskList> jobOwner(Map map) {
        return wrapper(mHttpAPI.jobOwner(ContainValue.userLogin.getData().getUserId() + "",  addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<TaskList> jobCreat(Map map) {
        return wrapper(mHttpAPI.jobCreat(ContainValue.userLogin.getData().getUserId() + "", addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<BaseBack> jobAccept(Map map) {
        return wrapper(mHttpAPI.jobAccept(addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<BaseBack> jobFinish(Map map) {
        return wrapper(mHttpAPI.jobFinish(addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    //***************************************job******************************//


    //***************************************user******************************//
    public Observable<UserInfo> getUserInfo(String uid) {
        return wrapper(mHttpAPI.getUserInfo(uid)).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<BaseBack> updateWorkState(String uid, Map map) {
        return wrapper(mHttpAPI.updateWorkState(uid, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<UserInfo> setMyInfo(String uid, Map map) {     //String userId, String nickName   userId, nickName
        return wrapper(mHttpAPI.setMyInfo(uid, map)).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<UserInfo> setMyAvater(String uid, MultipartBody.Part photo, RequestBody uid2) {     //String userId, String nickName   userId, nickName
        return wrapper(mHttpAPI.setMyAvater(uid, photo, uid2)).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<LoadFile> uploadFile(MultipartBody.Part file) {     //String userId, String nickName   userId, nickName
        return wrapper(mHttpAPI.uploadFile(file)).compose(SCHEDULERS_TRANSFORMER);
    }
    //***************************************user******************************//


    //***************************************org******************************//
    public Observable<AllNode> getAllNode(Map map) {     //String userId, String nickName   userId, nickName
        return wrapper(mHttpAPI.getAllNode(addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<BaseBack> getNodeByParent(String parrentId, Map map) {     //String userId, String nickName   userId, nickName
        return wrapper(mHttpAPI.getNodeByParent(parrentId, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    //***************************************org******************************//


    //***************************************project******************************//
    public Observable<UserInfo> addProject(Map map) {           //MultipartBody.Part json
        return wrapper(mHttpAPI.addProject(addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<ProjectEntity> getProjectOwn(String uid, Map map) {
        return wrapper(mHttpAPI.getProjectOwn(uid, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<ProjectEntity> getProjectParticipants(String uid, Map map) {
        return wrapper(mHttpAPI.getProjectParticipants(uid, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<ProjectEntity> getProjectStar(String uid, Map map) {
        return wrapper(mHttpAPI.getProjectStar(uid, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<BaseBack> report(String projectId, Map map) {
        return wrapper(mHttpAPI.report(addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<ProjectDetail> getProjectDetail(String uid, @QueryMap Map map) {
        return wrapper(mHttpAPI.getProjectDetail(uid, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<BaseBack> addStar(Map map) {
        return wrapper(mHttpAPI.addStar(addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<BaseBack> projectHurry(Map map) {
        return wrapper(mHttpAPI.projectHurry(addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<BaseBack> projectDone(Map map) {
        return wrapper(mHttpAPI.projectDone(addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<Comment> getProjectComment(String projectId, Map map) {
        return wrapper(mHttpAPI.getProjectComment(projectId, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<BaseBack> deletComment(String commentId, Map map) {
        return wrapper(mHttpAPI.deletComment(commentId, addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<BaseBack> deletProject(Map map) {
        return wrapper(mHttpAPI.deletProject(addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<BaseBack> addComment(Map map) {
        return wrapper(mHttpAPI.addComment(addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    public Observable<BaseBack> deleteStar(Map map) {
        return wrapper(mHttpAPI.deleteStar(addParams(map))).compose(SCHEDULERS_TRANSFORMER);
    }
    //***************************************project******************************//
    /**
     * 给任何Http的Observable加上通用的线程调度器
     */
    private static final ObservableTransformer SCHEDULERS_TRANSFORMER = new ObservableTransformer() {
        @Override
        public ObservableSource apply(@NonNull Observable upstream) {
            return upstream.subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread());
        }
    };

    private <T extends BaseBack> Observable<T> wrapper(Observable<T> resourceObservable) {
        return resourceObservable
                .flatMap(new Function<T, ObservableSource<? extends T>>() {
                    @Override
                    public ObservableSource<? extends T> apply(@NonNull T baseResponse) throws Exception {
                        return Observable.create(
                                new ObservableOnSubscribe<T>() {
                                    @Override
                                    public void subscribe(@NonNull ObservableEmitter<T> e) throws Exception {
                                        if (baseResponse.getResultCode() != 0) {
                                            ToastUtil.displayShortToast(baseResponse.getResultMsg());
                                            e.onComplete();
                                        } else {
                                            e.onNext(baseResponse);
                                        }
                                    }
                                });
                    }
                })
                .doOnError(new Consumer<Throwable>() {
                    @Override
                    public void accept(@NonNull Throwable e) throws Exception {
                        e.printStackTrace();
                        String errorText = "";
                        if (e instanceof HttpException) {
                            HttpException exception = (HttpException) e;
                            errorText = "网络错误";
                            return;
                        } else if (e instanceof UnknownHostException) {
                            KLog.i("请打开网络");
                            errorText = "请打开网络";
                        } else if (e instanceof SocketTimeoutException) {
                            KLog.i("请求超时");
                            errorText = "请求超时";
                        } else if (e instanceof ConnectException) {
                            KLog.i("连接失败");
                            errorText = "连接失败";
                        } else if (e instanceof HttpException) {
                            KLog.i("请求超时");
                            errorText = "请求超时";
                        } else {
                            KLog.i("请求失败");
                            errorText = "请求失败";
                        }
                        ToastUtil.displayShortToast(errorText);
                    }
                });
    }

    /**
     * 根据Http的response中关于状态码的描述，自定义生成本地的Exception
     *
     * @param resourceObservable
     * @param <T>
     * @return
     */
    private <T extends Object> Observable<T> wrapperObject(Observable<T> resourceObservable) {
        return resourceObservable
                .flatMap(new Function<T, ObservableSource<? extends T>>() {
                    @Override
                    public ObservableSource<? extends T> apply(@NonNull T baseResponse) throws Exception {
                        return Observable.create(
                                new ObservableOnSubscribe<T>() {
                                    @Override
                                    public void subscribe(@NonNull ObservableEmitter<T> e) throws Exception {
                                        if (baseResponse == null) {

                                        } else {
                                            //// TODO: 2017/6/8 没有做错误处理，因为现在后台返回的结果格式都不一样，等后台统一了返回再做处理
                                            e.onNext(baseResponse);
                                        }
                                    }
                                });
                    }
                })
                .doOnError(new Consumer<Throwable>() {
                    @Override
                    public void accept(@NonNull Throwable e) throws Exception {
                        e.printStackTrace();
                        String errorText = "";
                        if (e instanceof HttpException) {
                            HttpException exception = (HttpException) e;
                        } else if (e instanceof UnknownHostException) {
                            KLog.i("请打开网络");
                            errorText = "请打开网络";
                            return;
                        } else if (e instanceof SocketTimeoutException) {
                            KLog.i("请求超时");
                            errorText = "请求超时";
                        } else if (e instanceof ConnectException) {
                            KLog.i("连接失败");
                            errorText = "连接失败";
                        } else if (e instanceof HttpException) {
                            KLog.i("请求超时");
                            errorText = "请求超时";
                        } else {
                            KLog.i("请求失败");
                            errorText = "请求失败";
                        }
                        ToastUtil.displayShortToast(errorText);
                    }
                });
    }

    /**
     * 给任何Http的Observable加上在Service中运行的线程调度器
     */
    public static <T> ObservableTransformer<T, T> getSchedulerstransFormerToService() {
        return new ObservableTransformer<T, T>() {
            @Override
            public ObservableSource<T> apply(Observable<T> upstream) {
                return upstream.subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread());
            }
        };
    }

    //需要额外的添加其他的参数进去，所以把原有的参数和额外的参数通过这个方法一起添加进去.
    public static Map addParams(Map<String, String> data) {
        //添加统一的参数的地方
        data.put("platform", "Android");
        data.put("_userId", ContainValue.userLogin.getData().getUserId() + "");
        data.put("userId", ContainValue.userLogin.getData().getUserId() + "");
        data.put("app_version", VersionUtil.getAppVersionName(AppConfig.getInstance()));
        return data;
    }

    //需要额外的添加其他的参数进去，所以把原有的参数和额外的参数通过这个方法一起添加进去.
    public static String addParams2String(Map<String, String> data) {
        //添加统一的参数的地方
        JsonObject js = new JsonObject();
        LinkedHashMap<String, String> stampData = (LinkedHashMap<String, String>) MD5Util.getStamp();
        Iterator<Map.Entry<String, String>> itStamp = stampData.entrySet().iterator();
        while (itStamp.hasNext()) {
            Map.Entry<String, String> entry = itStamp.next();
            js.addProperty(entry.getKey(), entry.getValue());
        }
        Iterator<Map.Entry<String, String>> it = data.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, String> entry = it.next();
            js.addProperty(entry.getKey(), entry.getValue());
        }
        String temp = js.toString();
        byte[] encryptBytes = new byte[0];
        try {
            encryptBytes = RSAUtil.encryptByPublicKey(temp.getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        }
        String encryStr = new String(encryptBytes);
        return encryStr;
    }

}
